﻿using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UMLSynthesizer.CSharpAnalyzer.Expressions;
using UMLSynthesizer.LangAnalyzer.Expressions;
using UMLSynthesizer.LangAnalyzer.Statements;

namespace UMLSynthesizer.CSharpAnalyzer.Helpers
{
    class CSharpExpressionHelper
    {
        #region Fields

        IExpressionBase parentExpression;
        IStatementBase parentStatement;
        IEnumerable<IExpressionBase> _expressions;

        #endregion

        #region Delegate

        Func<ExpressionSyntax> GetExpressionNode;
        Func<IEnumerable<ExpressionSyntax>> GetExpressionNodes;

        #endregion

        #region Constructor

        public CSharpExpressionHelper(IExpressionBase parentExpression, IStatementBase parentStatement)
        {
            this.parentExpression = parentExpression;
            this.parentStatement = parentStatement;
        }
        public CSharpExpressionHelper(IExpressionBase parentExpression, IStatementBase parentStatement,
           Func<ExpressionSyntax> getExpressionNode)
        {
            this.parentExpression = parentExpression;
            this.parentStatement = parentStatement;
            this.GetExpressionNode = getExpressionNode;
        }
        public CSharpExpressionHelper(IExpressionBase parentExpression, IStatementBase parentStatement,
            Func<IEnumerable<ExpressionSyntax>> getExpressionNodes)
        {
            this.parentExpression = parentExpression;
            this.parentStatement = parentStatement;
            this.GetExpressionNodes = getExpressionNodes;
        }

        #endregion

        #region Properties

        public IExpressionBase Expression
        {
            get
            {
                if (GetExpressionNode == null)
                    throw new InvalidOperationException("Impossible to get single expression because GetExpressionNode is null");

                if (_expressions == null)
                {
                    var expressionNode = GetExpressionNode();
                    if (expressionNode == null)
                        _expressions = new IExpressionBase[0];
                    else
                    {
                        var csharpExpression = CreateCSharpExpression(expressionNode);
                        if (csharpExpression != null)
                            _expressions = new IExpressionBase[] { csharpExpression };
                        else
                            _expressions = new IExpressionBase[0];
                    }
                }

                return _expressions.FirstOrDefault();
            }
        }
        public IEnumerable<IExpressionBase> Expressions
        {
            get
            {
                if (GetExpressionNodes == null)
                    throw new InvalidOperationException("Impossible to get multiple expressions because GetExpressionNodes is null");

                if (_expressions == null)
                {
                    var expressionNodes = GetExpressionNodes();
                    List<IExpressionBase> expressionList = new List<IExpressionBase>();
                    if(expressionNodes != null)
                        foreach (var expression in expressionNodes)
                        {
                            var csharpExpression = CreateCSharpExpression(expression);
                            if (csharpExpression != null)
                                expressionList.Add(csharpExpression);
                        }
                    _expressions = expressionList;
                }

                return _expressions;
            }
        }

        public IExpressionBase CreateCSharpExpression(ExpressionSyntax expression)
        {
            switch (expression.CSharpKind())
            {
                case SyntaxKind.AddAssignmentExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.AddAssign);
                case SyntaxKind.AddExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.Add);
                case SyntaxKind.AddressOfExpression:
                    break;
                case SyntaxKind.AndAssignmentExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.AndAssign);
                case SyntaxKind.AnonymousMethodExpression:
                    break;
                case SyntaxKind.AnonymousObjectCreationExpression:
                    break;
                case SyntaxKind.ArgListExpression:
                    break;
                case SyntaxKind.ArrayCreationExpression:
                    return new CSharpArrayCreationExpression(expression as ArrayCreationExpressionSyntax,
                        parentExpression, parentStatement);
                case SyntaxKind.ArrayInitializerExpression:
                    return new CSharpInitializerExpression(expression as InitializerExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.ArrayInitializer);
                case SyntaxKind.AsExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.As);
                case SyntaxKind.AwaitExpression:
                    break;
                case SyntaxKind.BaseExpression:
                    break;
                case SyntaxKind.BitwiseAndExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.BitwiseAnd);
                case SyntaxKind.BitwiseNotExpression:
                    return new CSharpPrefixUnaryExpression(expression as PrefixUnaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.BitwiseNot);
                case SyntaxKind.BitwiseOrExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.BitwiseOr);
                case SyntaxKind.CastExpression:
                    return new CSharpCastExpression(expression as CastExpressionSyntax,
                        parentExpression, parentStatement);
                case SyntaxKind.CharacterLiteralExpression:
                    return new CSharpLiteralExpression(expression as LiteralExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.CharacterLiteral);
                case SyntaxKind.CheckedExpression:
                    break;
                case SyntaxKind.CoalesceExpression:
                    break;
                case SyntaxKind.CollectionInitializerExpression:
                    break;
                case SyntaxKind.ComplexElementInitializerExpression:
                    break;
                case SyntaxKind.ConditionalAccessExpression:
                    break;
                case SyntaxKind.ConditionalExpression:
                    return new CSharpConditionalExpression(expression as ConditionalExpressionSyntax,
                        parentExpression, parentStatement);
                case SyntaxKind.DeclarationExpression:
                    break;
                case SyntaxKind.DefaultExpression:
                    break;
                case SyntaxKind.DivideAssignmentExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.DivideAssign);
                case SyntaxKind.DivideExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.Divide);
                case SyntaxKind.ElementAccessExpression:
                    break;
                case SyntaxKind.ElementBindingExpression:
                    break;
                case SyntaxKind.EqualsExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.Equals);
                case SyntaxKind.ExclusiveOrAssignmentExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.ExclusiveOrAssign);
                case SyntaxKind.ExclusiveOrExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.ExclusiveOr);
                case SyntaxKind.FalseLiteralExpression:
                    return new CSharpLiteralExpression(expression as LiteralExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.FalseLiteral);
                case SyntaxKind.GreaterThanExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.GreaterThan);
                case SyntaxKind.GreaterThanOrEqualExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.GreaterThanOrEqual);
                case SyntaxKind.IdentifierName:
                    return new CSharpElementReferenceExpression(expression as IdentifierNameSyntax,
                        parentExpression, parentStatement);
                case SyntaxKind.ImplicitArrayCreationExpression:
                    break;
                case SyntaxKind.InvocationExpression:
                    return new CSharpInvocationExpression(expression as InvocationExpressionSyntax,
                        parentExpression, parentStatement);
                case SyntaxKind.IsExpression:
                    break;
                case SyntaxKind.LeftShiftAssignmentExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.LeftShiftAssign);
                case SyntaxKind.LeftShiftExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.LeftShift);
                case SyntaxKind.LessThanExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.LessThan);
                case SyntaxKind.LessThanOrEqualExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.LessThanOrEqual);
                case SyntaxKind.LogicalAndExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.LogicalAnd);
                case SyntaxKind.LogicalNotExpression:
                    return new CSharpPrefixUnaryExpression(expression as PrefixUnaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.LogicalNot);
                case SyntaxKind.LogicalOrExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.LogicalOr);
                case SyntaxKind.MakeRefExpression:
                    break;
                case SyntaxKind.MemberBindingExpression:
                    break;
                case SyntaxKind.ModuloAssignmentExpression:
                    break;
                case SyntaxKind.ModuloExpression:
                    break;
                case SyntaxKind.MultiplyAssignmentExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.MultiplyAssign);
                case SyntaxKind.MultiplyExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.Multiply);
                case SyntaxKind.NotEqualsExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.NotEquals);
                case SyntaxKind.NullLiteralExpression:
                    return new CSharpLiteralExpression(expression as LiteralExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.NullLiteral);
                case SyntaxKind.NumericLiteralExpression:
                    return new CSharpLiteralExpression(expression as LiteralExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.NumericLiteral);
                case SyntaxKind.ObjectCreationExpression:
                    return new CSharpObjectCreationExpression(expression as ObjectCreationExpressionSyntax,
                        parentExpression, parentStatement);
                case SyntaxKind.ObjectInitializerExpression:
                    return new CSharpInitializerExpression(expression as InitializerExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.ObjectInitializer);
                case SyntaxKind.OmittedArraySizeExpression:
                    break;
                case SyntaxKind.OrAssignmentExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.OrAssign);
                case SyntaxKind.ParenthesizedExpression:
                    return new CSharpParenthesizedExpression(expression as ParenthesizedExpressionSyntax,
                        parentExpression, parentStatement);
                case SyntaxKind.ParenthesizedLambdaExpression:
                    break;
                case SyntaxKind.PointerIndirectionExpression:
                    break;
                case SyntaxKind.PointerMemberAccessExpression:
                    break;
                case SyntaxKind.PostDecrementExpression:
                    return new CSharpPostfixUnaryExpression(expression as PostfixUnaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.PostDecrement);
                case SyntaxKind.PostIncrementExpression:
                    return new CSharpPostfixUnaryExpression(expression as PostfixUnaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.PostIncrement);
                case SyntaxKind.PreDecrementExpression:
                    return new CSharpPrefixUnaryExpression(expression as PrefixUnaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.PreDecrement);
                case SyntaxKind.PreIncrementExpression:
                    return new CSharpPrefixUnaryExpression(expression as PrefixUnaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.PreIncrement);
                case SyntaxKind.QueryExpression:
                    break;
                case SyntaxKind.RefTypeExpression:
                    break;
                case SyntaxKind.RefValueExpression:
                    break;
                case SyntaxKind.RightShiftAssignmentExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.RightShiftAssign);
                case SyntaxKind.RightShiftExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.RightShift);
                case SyntaxKind.SimpleAssignmentExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.Assign);
                case SyntaxKind.SimpleLambdaExpression:
                    break;
                case SyntaxKind.SimpleMemberAccessExpression:
                    return new CSharpMemberAccessExpression(expression as MemberAccessExpressionSyntax,
                        parentExpression, parentStatement);
                case SyntaxKind.SizeOfExpression:
                    return new CSharpSizeOfExpression(expression as SizeOfExpressionSyntax,
                        parentExpression, parentStatement);
                case SyntaxKind.StackAllocArrayCreationExpression:
                    break;
                case SyntaxKind.StringLiteralExpression:
                    return new CSharpLiteralExpression(expression as LiteralExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.StringLiteral);
                case SyntaxKind.SubtractAssignmentExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.SubtractAssign);
                case SyntaxKind.SubtractExpression:
                    return new CSharpBinaryExpression(expression as BinaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.Subtract);
                case SyntaxKind.ThisExpression:
                    return new CSharpThisExpression(expression as ThisExpressionSyntax,
                        parentExpression, parentStatement);
                case SyntaxKind.TrueLiteralExpression:
                    return new CSharpLiteralExpression(expression as LiteralExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.TrueLiteral);
                case SyntaxKind.TypeOfExpression:
                    break;
                case SyntaxKind.UnaryMinusExpression:
                    return new CSharpPrefixUnaryExpression(expression as PrefixUnaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.Negate);
                case SyntaxKind.UnaryPlusExpression:
                    return new CSharpPrefixUnaryExpression(expression as PrefixUnaryExpressionSyntax,
                        parentExpression, parentStatement, ExpressionKinds.Plus);
                case SyntaxKind.UncheckedExpression:
                    break;
            }

            return new CSharpGenericExpression(expression, parentExpression, parentStatement);
        }

        #endregion
    }
}
